////
//
/// \file   ./src/ws_parser.cpp
//
//  Copyright 2007 Chandler Carruth
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License. You may obtain a copy
//  of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
//  License for the specific language governing permissions and limitations
//  under the License.
//
////

#include  <inc/parser/ws_parser.hpp>

#include  <string>

#include  <boost/regex.hpp>
#include  <boost/scoped_ptr.hpp>

namespace inc {
namespace parser {

using boost::match_results;
using boost::regex;
using boost::scoped_ptr;

/// \brief  Protected constructor for dependency injection
/// \author Chandler Carruth
/// \date   2008.04.28
///
/// This constructor is exclusively for doing dependency injection, either in 
/// the context of a variant subclass, or more likely for the purposes of 
/// mocking and unit testing. It takes ownership of all three parameters.
///
/// \param[in]  ws_regex The desired basic whitespace regex
/// \param[in]  strict_ws_regex The desired strict whitespace regex
/// \param[in]  newline_regex The desired newline whitespace regex

WSParser::WSParser( regex *ws_regex,
                    regex *strict_ws_regex,
                    regex *eol_ws_regex )
    : ws_regex(ws_regex),
      strict_ws_regex(strict_ws_regex),
      eol_ws_regex(eol_ws_regex)
{}

/// \brief  Parse out whitespace
/// \author Chandler Carruth
/// \date   2007.06.19
///
/// This parses out whitespace, returning true if some whitespace was found.  
/// This version of the routine is extremely relaxed, and will allow almost any 
/// form of whitespace, including vertical whitespace.
///
/// TODO(chandlerc): This routine needs to be updated to manage manually 
/// matching UTF-8 encoded characters.
///
/// \param[in]  first Beginning of region to parse
/// \param[in]  last End of region to parse
/// \param[out] last_parsed The end of the parse

bool
WSParser::parse_ws( const iterator& first,
                    const iterator& last,
                    iterator& last_parsed )
{
  if( !ws_regex )
    ws_regex.reset( new regex( "[\\s\\h\\v\\R]+" ) );

  //  We always clear out the last_parsed to enable optional WS handling.
  last_parsed = first;

  match_results<iterator> m;
  if( regex_search( first, last, m, *ws_regex, boost::match_continuous ) )
  {
    last_parsed = m.suffix().first;
    return true;
  }
  return false;
}

/// \brief  Parse out *horizontal* whitespace
/// \author Chandler Carruth
/// \date   2007.06.19
///
/// This parses out horizontal whitespace only, and returns true when it has 
/// successfully parsed some. This does not include any vertical white space, 
/// and can be used in places where vertical white space would be particularly 
/// confusing or ambiguous.
///
/// TODO(chandlerc): This routine needs to be updated to manage manually 
/// matching UTF-8 encoded characters.
///
/// \param[in]  first Beginning of region to parse
/// \param[in]  last End of region to parse
/// \param[out] last_parsed The end of the parse

bool
WSParser::parse_strict_ws( const iterator& first,
                           const iterator& last,
                           iterator& last_parsed )
{
  if( !strict_ws_regex )
    strict_ws_regex.reset( new regex( "[ \\t]+" ) );

  //  We always clear out the last_parsed to enable optional WS handling.
  last_parsed = first;

  match_results<iterator> m;
  if( regex_search( first, last, m, *strict_ws_regex, boost::match_continuous ) )
  {
    last_parsed = m.suffix().first;
    return true;
  }
  return false;
}

/// \brief  Parse out line termination, or vertical whitespace
/// \author Chandler Carruth
/// \date   2007.06.19
///
/// This parses the termination of a line, or vertical whitespace. We do this 
/// with a greedy whitespace system to consume as much whitespace as possible 
/// in one parse step. This is a line *termination* parse however, because we 
/// might not parse *any* characters, and succeed 
///
/// TODO(chandlerc): This routine needs to be updated to manage manually 
/// matching UTF-8 encoded characters.
///
/// \param[in]  first Beginning of region to parse
/// \param[in]  last End of region to parse
/// \param[out] last_parsed The end of the parse

//  HACK!!!
//
//  Ok, let us bravely enter the world of constructing UTF-8 encoded
//  string literals in portable C++ code. Wait, you can't. Let us
//  bravely then enter the world of hacking together UTF-8 encoded
//  string literals because thats the only blasted way we can do things
//  with this asinine language. Backwards compatibility can bite me.
#define   LF_CSTR     "\x0A"
#define   CR_CSTR     "\x0D"
#define   CRLF_CSTR   "\x0D\x0A"
#define   NEL_CSTR    "\xC2\x85"
#define   FF_CSTR     "\x0C"
#define   LS_CSTR     "\xE2\x80\xA8"
#define   PS_CSTR     "\xE2\x80\xA9"

bool
WSParser::parse_eol_ws( const iterator& first,
                        const iterator& last,
                        iterator& last_parsed )
{
  if( !eol_ws_regex )
    eol_ws_regex.reset( new regex( "[ \\t]*\\n[ \\t\\n]*" ) );
#if 0
  if( !eol_ws_regex )
    eol_ws_regex.reset( new regex(
        "\\h*[" LF_CSTR CR_CSTR CRLF_CSTR NEL_CSTR FF_CSTR LS_CSTR PS_CSTR "]"
        "[" LF_CSTR CR_CSTR CRLF_CSTR NEL_CSTR FF_CSTR LS_CSTR PS_CSTR "\\h]*"
        ) );
#endif

  //  We always clear out the last_parsed to enable optional WS handling.
  last_parsed = first;

  match_results<iterator> m;
  if( regex_search( first, last, m, *eol_ws_regex, boost::match_continuous ) )
  {
    last_parsed = m.suffix().first;
    return true;
  }
  return false;
}

} //  end parser namespace
} //  end inc namespace
